
//#include <winsock.h>
#include <winsock2.h>
#include <process.h>
#include <assert.h>
#include "Engine.h"


CProxyEngine& CProxyEngine::GetInstance()
{
	static CProxyEngine m_Engine;
	return m_Engine;
}

CProxyEngine::CProxyEngine()
{
	mEngineData.ProxyInfo.mode = 1;
	mEngineData.ProxyInfo.proxyPort = 5000;
	mEngineData.ProxyInfo.maxPool = 5;

	memset(&mEngineData.HttpProxyData, 0, sizeof(HTTP_PROXY_DATA));

	// initialize control variables
	mIsDestroy = true;
	mIsStart = false;
}

bool CProxyEngine::InitProxyEngine(PROXY_INFO proxyInfo)
{
	if(proxyInfo.maxPool > 20 ||
		(proxyInfo.mode != 1 && proxyInfo.mode != 2) ||
		(proxyInfo.proxyPort > 0xffff || proxyInfo.proxyPort < 0x1))
		return false;

	mPool = CThreadPool::GetInstance();

	mEngineData.ProxyInfo = proxyInfo;

	if(proxyInfo.mode == PM_CONTROL) {
		mThreadMutex = CreateMutex(NULL, FALSE, (LPCWSTR)"ThreadMutex");
//		mTrapMode = TRAP_NONE;
//		mEngineData.pTrapMode = &mTrapMode;
//		mReqPassEvt = CreateEvent(NULL, FALSE, FALSE, (LPCWSTR)"ReqPassEvt");
//		mResPassEvt = CreateEvent(NULL, FALSE, FALSE, (LPCWSTR)"ResPassEvt");
//		mDataStructMutex = CreateMutex(NULL, FALSE, (LPCWSTR)"ListMutex");
//		mEngineData.ReqResList = &mReqResList; 
	}

	return true;
}

bool CProxyEngine::DestroyProxyEngine()
{
	if(mIsDestroy || !mIsStart)
		return false;

//	CloseHandle(mReqPassEvt);
//	CloseHandle(mResPassEvt);
//	CloseHandle(mDataStructMutex);
//	if(!mReqResList.empty())
//		mReqResList.clear();
	CloseHandle(mThreadMutex);

	mPool->destroyPool();

	mIsDestroy = true;
	mIsStart = false;

	return true;
}

///////////////////////////////////////////////////////////////
//
// This function is responsible for seting up the networking and 
// making a thread pool properly. 
// 
int CProxyEngine::proxyStart()
{
	if(mIsStart)
		return 0;

	// Initializate the Thread Pool
	mPool->initPool(mEngineData.ProxyInfo.maxPool);

	mEngineThread = (HANDLE)_beginthreadex(NULL, 0, EngineThreadFunc, this, 0, (unsigned *)&mEngineThreadId);
	if(mEngineThread == NULL) {
		fprintf(stderr, "Failed to create a thread for the engine!\n");
		return 0;
	}

	mIsStart = true;
	mIsDestroy = false;

	return 1;
}

int CProxyEngine::proxyStop()
{
	return 0;
}

int CProxyEngine::proxySuspend()
{
	return 0;
}

int CProxyEngine::proxySingleStep()
{
	return 0;
}

int CProxyEngine::proxyDrop()
{
	return 0;
}

////////////////////////////////////////////////////////////////
//
// This function is the thread function for the engine.
//
unsigned __stdcall CProxyEngine::EngineThreadFunc(void *pArg)
{
	CProxyEngine *pEngineInst = (CProxyEngine *)pArg;
	
	// Initialize a networking
	WSADATA	wsaData;
	int		ret;
	ret = WSAStartup(MAKEWORD(2,2), &wsaData);
	if(ret != 0) {
		fprintf(stderr, "WSAStartup error\n");
		return 1;
	}

	SOCKET		proxySock;
	SOCKADDR_IN	proxyAddr;
	
	//
	// make a socket for between client and proxy
	//
//	proxySock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	proxySock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	if(proxySock == INVALID_SOCKET) {
		fprintf(stderr, "creating proxySock is failed(ErrorCode: %d)\n", WSAGetLastError());
		return 1;
	}

	BOOL bValid = TRUE;
	ret = setsockopt(proxySock, SOL_SOCKET, SO_REUSEADDR, (const char *)&bValid, sizeof(bValid));
	if(ret == SOCKET_ERROR) {
		fprintf(stderr, "setsockopt() function is failed: SO_REUSEADDR(ErrorCode: %d)\n", WSAGetLastError());
		return 1;
	}

	memset(&proxyAddr, 0, sizeof(proxyAddr));
	proxyAddr.sin_family = AF_INET;
	proxyAddr.sin_port = htons(pEngineInst->mEngineData.ProxyInfo.proxyPort);
	proxyAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

	ret = bind(proxySock, (SOCKADDR *)&proxyAddr, sizeof(proxyAddr));
	if(ret == SOCKET_ERROR) {
		fprintf(stderr, "bind() function is failed. (ErrorCode: %d)\n", WSAGetLastError());
		return 1;
	}

	listen(proxySock, 5);

	/* LOOP
	 * The following routine accepts a connection of any client. Once making a connection,
	 * then, it makes a job for http proxy. finally, a job crafted is registered to the pool of threads. */
	while(1) {
		printf("Waiting for client...\n");
		
		SOCKET		cpSock;
		SOCKADDR_IN	clientAddr;
		int clientAddrLen = sizeof(clientAddr);
		
		cpSock = accept(proxySock, (SOCKADDR *)&clientAddr, &clientAddrLen);
		if(cpSock == INVALID_SOCKET) {
			fprintf(stderr, "accept() function is failed. (ErrorCode: %d)\n", WSAGetLastError());
			break;
		}

		// make a job
		CPoolJob *httpJob = new CPJNonControl[1];
		assert(httpJob != NULL);

		ENGINE_DATA	ed;
		memset(&ed, 0, sizeof(ed));
		ed = pEngineInst->mEngineData;
		ed.HttpProxyData.cpSock = cpSock;
		ed.HttpProxyData.clientAddr = clientAddr;
		ed.HttpProxyData.proxyAddr = proxyAddr;

		httpJob->SetEngineData(ed);
		
		// assign job to any thread in the thread pool
		pEngineInst->mPool->registJob(httpJob);
	}

	// epilog
	closesocket(proxySock);
	WSACleanup();

	return 0;
}


CProxyEngine::~CProxyEngine()
{
	DestroyProxyEngine();
}

